package com.thinkingcoder.tcconfig.client.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.TypeReference;
import com.thinkingcoder.common.util.HttpUtils;
import com.thinkingcoder.tcconfig.client.config.ConfigMeta;
import com.thinkingcoder.tcconfig.client.listener.TcConfigChangListener;
import com.thinkingcoder.tcconfig.client.listener.TcConfigChangeEvent;

import lombok.extern.slf4j.Slf4j;

/**
 * default impl for tc repository.
 */
@Slf4j
public class TcRepositoryImpl implements TcRepository {

    private ConfigMeta configMeta;
    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

    Map<String, Long> versionMap = new HashMap<>();
    Map<String, Map<String, String>> configMap = new HashMap<>();

    private List<TcConfigChangListener> changListeners = new ArrayList<>();

    public TcRepositoryImpl(ConfigMeta configMeta) {
        this.configMeta = configMeta;
        executor.scheduleWithFixedDelay(this::deferredHeartbeat, 1_000, 1_000, TimeUnit.MILLISECONDS);
    }

    @Override
    public Map<String, String> getConfig() {
        String key = configMeta.generateKey();
        if (configMap.containsKey(key)) {
            return configMap.get(key);
        }
        return findAllNewConfigs();
    }

    private Map<String, String> findAllNewConfigs() {
        log.info("[TC-CONFIG] List all configs from tc config server.");
        List<Configs> configs = HttpUtils.httpGet(configMeta.listPath(), new TypeReference<List<Configs>>() {});
        Map<String, String> resultMap = new HashMap<>();
        configs.forEach(entry -> resultMap.put(entry.getPKey(), entry.getPVal()));
        return resultMap;
    }

    private void heartbeat() {
        String key = configMeta.generateKey();
        Long oldVersion = versionMap.getOrDefault(key, -1L);
        Long newVersion = HttpUtils.httpGet(configMeta.versionPath(), new TypeReference<Long>() {});
        compareVersion(newVersion, oldVersion, key);
    }

    private void deferredHeartbeat() {
        String key = configMeta.generateKey();
        Long oldVersion = versionMap.getOrDefault(key, -1L);
        Long newVersion = HttpUtils.httpGet(configMeta.deferredVersionPath(oldVersion), new TypeReference<Long>() {});
        compareVersion(newVersion, oldVersion, key);
    }

    private void compareVersion(Long newVersion, Long oldVersion, String key) {
        log.info("[TC-CONFIG] newVersion : {} , oldVersion : {} .", newVersion, oldVersion);
        if (newVersion > oldVersion) {
            log.info("[TC-CONFIG] Need update new configs.");
            versionMap.put(key, newVersion);
            Map<String, String> allNewConfigs = findAllNewConfigs();
            configMap.put(key, allNewConfigs);
            changListeners
                .forEach(changListener -> changListener.onChange(new TcConfigChangeEvent(configMeta, allNewConfigs)));
        }
    }

    @Override
    public void addChangeListener(TcConfigChangListener changListener) {
        changListeners.add(changListener);
    }
}
